﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace consoleTesting
{
    public class Helper
    {
    }

    public enum Direction
    {
        UP,
        DOWN,
        DODGE
    }

    public enum Symbol
    { 
        EUR
    }

    public enum OrderType
    { 
        OP_BUY,// - позиция на покупку,
        OP_SELL,// - позиция на продажу,
        OP_BUYLIMIT,// - отложенный ордер на покупку по достижении заданного уровня, текущая цена выше уровня,
        OP_BUYSTOP,// - отложенный ордер на покупку по достижении заданного уровня, текущая цена ниже уровня,
        OP_SELLLIMIT,// - отложенный ордер на продажу по достижении заданного уровня, текущая цена ниже уровня,
        OP_SELLSTOP// - отложенный ордер на продажу по достижении заданного уровня, текущая цена выше уровня.
    }

    public enum Period
    {
        PERIOD_M1,
        PERIOD_M15
    }

    public enum HourPart
    { 
        One, Two, Three, Four, None
    }

    public enum CandlePositionInPeriod { First, Last, None}

    public class NetWSRTHelper
    {


        #region 

        int iMA_Period = 75; //--- (60 5 100)
        int iCCI_Period = 18; //--- (10 2 30)
        int iATR_Period = 14; //--- (10 2 30) (!) Можно не оптить
        int iWPR_Period = 11; //--- (10 1 20)

        #endregion

        #region

        public void SerializeM15Candles()
        {
            string fileName = String.Format("WSRT3_8/WSRT_M15.xml");
            Stream stream = File.Open(fileName, FileMode.Create);

            System.Xml.Serialization.XmlSerializer xmlSerM15 = new System.Xml.Serialization.XmlSerializer(M15_CandleWSRTs.GetType());
            xmlSerM15.Serialize(stream, M15_CandleWSRTs);
        }


        #endregion

        private static int Digits = 4;

        public List<Candle> M1_Candles = new List<Candle>();
        public List<CandleWSRT> M15_CandleWSRTs = new List<CandleWSRT>();



        Candle CurrentCandle = new Candle();

        public NetWSRTHelper() 
        {

        }

        public void AddMinuteCandleToCollection(Candle c)
        {
            M1_Candles.Add(c);

        }

        public void WorkM15Candles()
        {
            foreach (Candle c in M1_Candles)
            {
                if (c.Time < new DateTime(2011, 09, 01)||c.Time>new DateTime(2011,9,10))
                    continue;

                CurrentCandle = c;
                BuildM15CandleWSRT(c);
            }
        }

        private int candleIndex=0;

        private void BuildM15CandleWSRT(Candle c)
        {
            if (M1_Candles.Count < 15)
                return;

            HourPart part = HourPart.None;

            if (c.Time == new DateTime(2011, 9, 2, 1, 1, 0))
           { 
                
            }

            if (c.Time.Minute >= 0 && c.Time.Minute < 15 )
                part=HourPart.One;

            if (c.Time.Minute >= 15 && c.Time.Minute < 30 )
                part=HourPart.Two;

            if (c.Time.Minute >= 30 && c.Time.Minute < 45 )
                part=HourPart.Three;

            if (c.Time.Minute >= 45 && c.Time.Minute <= 59)
                part=HourPart.Four;

            CandleWSRT m15 = new CandleWSRT();
            m15.Time = new DateTime(CurrentCandle.Time.Year);

            switch (part)
            { 
                case HourPart.One:
                    m15.Time = new DateTime(CurrentCandle.Time.Year, CurrentCandle.Time.Month, CurrentCandle.Time.Day,CurrentCandle.Time.Hour,0,0);
                    break;
                case HourPart.Two:
                    m15.Time = new DateTime(CurrentCandle.Time.Year, CurrentCandle.Time.Month, CurrentCandle.Time.Day,CurrentCandle.Time.Hour,15,0);
                    break;
                case HourPart.Three:
                    m15.Time = new DateTime(CurrentCandle.Time.Year, CurrentCandle.Time.Month, CurrentCandle.Time.Day, CurrentCandle.Time.Hour, 30, 0);
                    break;
                case HourPart.Four:
                    m15.Time = new DateTime(CurrentCandle.Time.Year, CurrentCandle.Time.Month, CurrentCandle.Time.Day, CurrentCandle.Time.Hour, 45, 0);
                    break;
                default:
                    break;
            }

            if (M15_CandleWSRTs.FindAll(x => x.Time == m15.Time).Count == 0)
            {
                m15.Open = GetM15Open(part);
                m15.High = GetM15High(part);
                m15.Low = GetM15Low(part);
                m15.Close = GetM15Close(part);

                if (m15.Open > m15.Close)
                    m15.Direction = Direction.DOWN;
                if (m15.Open < m15.Close)
                    m15.Direction = Direction.UP;
                if (m15.Open == m15.Close)
                    m15.Direction = Direction.DODGE;

                m15.BodyWidth = Math.Round((Math.Abs(m15.Open - m15.Close) * 10000), 2);

                m15.CandleIndex = candleIndex;
                M15_CandleWSRTs.Add(m15);
                candleIndex++;

                if (M15_CandleWSRTs.Count >= 100)
                {
                    m15.iATR_Signal = iATR(Period.PERIOD_M15, iATR_Period, 0);
                    m15.iMA_Signal = iMA(Period.PERIOD_M15, iMA_Period, 0);
                    m15.iWPR_Signal = iWPR(Period.PERIOD_M15, iWPR_Period, 0);
                    m15.iCCI_Signal = iCCI(Period.PERIOD_M15, iCCI_Period, 0);
                }

                
            }
        }

        private decimal GetM15Open(HourPart part)
        {
            List<Candle> lst = ListOfM1CandlesForM15Build(part);

            decimal open = lst.First().Open;

            return open;
        }

        private decimal GetM15Close(HourPart part)
        {
            List<Candle> lst = ListOfM1CandlesForM15Build(part);

            decimal close = lst.Last().Close;

            return close;
        }

        private decimal GetM15Low(HourPart part)
        {
            List<Candle> lst = ListOfM1CandlesForM15Build(part);

            decimal low = lst.Min(x => x.Low);

            return low;
        }

        private decimal GetM15High(HourPart part)
        {
            List<Candle> lst = ListOfM1CandlesForM15Build(part);

            decimal high = lst.Max(x => x.High);

            return high;
        }

        private List<Candle> ListOfM1CandlesForM15Build(HourPart part)
        {
            List<Candle> lst = new List<Candle>();

            DateTime start = new DateTime();
            DateTime end = new DateTime();
            switch (part)
            {
                case HourPart.One:
                    start = new DateTime(CurrentCandle.Time.Year, CurrentCandle.Time.Month, CurrentCandle.Time.Day, CurrentCandle.Time.Hour, 0, 0);
                    end = new DateTime(CurrentCandle.Time.Year, CurrentCandle.Time.Month, CurrentCandle.Time.Day, CurrentCandle.Time.Hour, 15, 0);
                    break;
                case HourPart.Two:
                    start = new DateTime(CurrentCandle.Time.Year, CurrentCandle.Time.Month, CurrentCandle.Time.Day, CurrentCandle.Time.Hour, 15, 0);
                    end = new DateTime(CurrentCandle.Time.Year, CurrentCandle.Time.Month, CurrentCandle.Time.Day, CurrentCandle.Time.Hour, 30, 0);
                    break;
                case HourPart.Three:
                    start = new DateTime(CurrentCandle.Time.Year, CurrentCandle.Time.Month, CurrentCandle.Time.Day, CurrentCandle.Time.Hour, 30, 0);
                    end = new DateTime(CurrentCandle.Time.Year, CurrentCandle.Time.Month, CurrentCandle.Time.Day, CurrentCandle.Time.Hour, 45, 0);
                    break;
                case HourPart.Four:
                    start = new DateTime(CurrentCandle.Time.Year, CurrentCandle.Time.Month, CurrentCandle.Time.Day, CurrentCandle.Time.Hour, 45, 0);
                    end = new DateTime(CurrentCandle.Time.Year, CurrentCandle.Time.Month, CurrentCandle.Time.Day, CurrentCandle.Time.Hour, 59, 0);
                    break;
                default: break;
            }

            if (part != HourPart.Four)
            {
                lst = M1_Candles.FindAll(x => x.Time >= start && x.Time < end);
            }
            else
            {
                lst = M1_Candles.FindAll(x => x.Time >= start && x.Time <= end);
            }

            if (lst.Count == 0)
            { 

            }

            return lst;
        }

        private CandleWSRT CandleWSRTForFormula(Period timeframe, int shift)
        {
            CandleWSRT c = new CandleWSRT();

            switch (timeframe)
            {
                case Period.PERIOD_M1:
                    //List<CandleWSRT> lst = ListOfM1CandlesForM15Build(part);
                   
                    //if (cPosition == CandlePositionInPeriod.First)
                    //    c = lst.First();

                    //if (cPosition == CandlePositionInPeriod.Last)
                    //    c = lst.Last();

                    break;
                case Period.PERIOD_M15:
                    //c = M15_CandleWSRTs[M15_CandleWSRTs.Count - 1 - shift];

                    if (shift > 0)
                        c = M15_CandleWSRTs.Find(x => x.CandleIndex == M15_CandleWSRTs.Last().CandleIndex - shift + 1);
                    else
                        c = M15_CandleWSRTs.Find(x => x.CandleIndex == M15_CandleWSRTs.Last().CandleIndex - shift);

                    break;
            }

            return c;
        }

        private double H(Period timeframe, int period, int shift)
        {
            double h = 0;
            List<CandleWSRT> lstCandleWSRTs = new List<CandleWSRT>();

            switch (timeframe)
            {
                case Period.PERIOD_M1:
                    //lstCandleWSRTs = M1_Candles;
                    break;
                case Period.PERIOD_M15:
                    lstCandleWSRTs = M15_CandleWSRTs;
                    break;
            }

            for (int i = lstCandleWSRTs.Count - period - shift; i < lstCandleWSRTs.Count - shift; i++)
            {
                if (h < (double)lstCandleWSRTs[i].High)
                    h = (double)lstCandleWSRTs[i].High;
            }

            return h;
        }

        private  double L(Period timeframe, int period, int shift)
        {
            double l = 10000;

            List<CandleWSRT> lstCandleWSRTs = new List<CandleWSRT>();

            switch (timeframe)
            {
                case Period.PERIOD_M1:
                    //lstCandleWSRTs = M1_Candles;
                    break;
                case Period.PERIOD_M15:
                    lstCandleWSRTs = M15_CandleWSRTs;
                    break;
            }

            for (int i = lstCandleWSRTs.Count - period - shift; i < lstCandleWSRTs.Count - shift; i++)
            {
                if (l > (double)lstCandleWSRTs[i].Low)
                    l = (double)lstCandleWSRTs[i].Low;
            }

            return l;
        }



        //Average true Range (Индикатор Среднего Истинного Диапазона) – это скользящее среднее истинного диапазона.
        //Истинный диапазон является максимумом из следующих величин:
        // * разность между предыдущей ценой закрытия и текущим максимумом;
        // * разность между максимумом и минимумом;
        // * разность между предыдущей ценой закрытия и текущим минимумом.
        //Расчетная формула ATR:
        //ATR = Moving Average(TRj, n),
        //где
        //TRj = максимальному из модулей трех значений
        //|High - Low|, |High - Closej-1|, |Low - Closej-1|. 
        public double iATR(Period timeframe, int period, int shift)//Расчет индикатора Average true Range. period - период усреднения для вычисления индикатора.
        {
            List<double> m = new List<double>();
            double iatr = 0;
            double iatrCurrent = 0;

            double module1 = 0;//Math.Abs(iClose(timeframe, period + shift + 1) - (double)CandleWSRTForFormula(timeframe, shift).High);
            double module2 = 0;//(double)Math.Abs(CandleWSRTForFormula(timeframe, shift).High - CandleWSRTForFormula(timeframe, shift).Low);
            double module3 = 0;//(double)Math.Abs(CandleWSRTForFormula(timeframe, shift + 1).Close - CandleWSRTForFormula(timeframe, shift).Low);
            double prevClose = 0;
            double curHigh = 0;
            double curLow = 0;

            for (int i = period + shift; i > shift; i--)
            {
                prevClose = (double)CandleWSRTForFormula(timeframe, i+1).Close;//iClose(timeframe, i + 1);
                curHigh=(double)CandleWSRTForFormula(timeframe, i).High;
                curLow=(double)CandleWSRTForFormula(timeframe, i).Low;

                module1 = Math.Abs( prevClose - curHigh);
                module2 = Math.Abs(curHigh - curLow);
                module3 = Math.Abs(prevClose - curLow);

                m = new List<double>();
                m.Add(module1);
                m.Add(module2);
                m.Add(module3);

                iatrCurrent = m.Max();
                iatr += iatrCurrent;
            }


            iatr = Math.Round(iatr / period, Digits);

            return iatr;
        }

        //%R = -1 * (H(n) – C(i)) / (H(n) – (L(n)) * 100,
        //где C(i) – цена закрытия текущего торгового периода (свечи), 
        //L(n) – минимальное значение цены за последние n свечей, 
        //H(n) – максимальное значение цены за последние n свечей, 
        //а n – период расчета индикатора. В качестве периода расчета индикатора в программах технического анализа обычно используется значение 14. 
        //Обратите внимание, что указанная выше формула действительно имеет много общего с формулой расчета индикатора Стохастик. Разница заключается лишь в том, что в случае со Стохастическим Осциллятором в расчет принимается разница между текущей ценой закрытия и минимумом торгового диапазона, а Процентный Диапазон Вильямса принимает в расчет разницу между максимальной ценой торгового диапазона и ценой закрытия. Знак -1 в формуле расчета используется для того, чтобы не терялся геометрический смысл зоны перекупленности (она должна быть вверху) и зоны перепроданности (она должна быть внизу). 
        //period 	  -   	Период(количество баров) для вычисления индикатора.
        //shift 	  -   	Индекс получаемого значения из индикаторного буфера (сдвиг относительно текущего бара на указанное количество периодов назад).
        public double iWPR(Period timeframe, int period, int shift)
        {
            double toReturn = 0;

            toReturn = -1 * ((H(timeframe, period, shift) - (double)CandleWSRTForFormula(timeframe, shift).Close) / (H(timeframe, period, shift) - L(timeframe, period, shift))) * 100;

            toReturn = Math.Round(toReturn, Digits);

            return toReturn;
        }


        //Сглаженное скользящее среднее (Smoothed Moving Average, SMMA)
        //Первое значение этой сглаженной рассчитывается, как и простая скользящая средняя (SMA):
        //S1 = S (CL (i), X)
        //SMMA1 = S1 / X
        //Второе и последующие скользящие средние рассчитываются по следующей формуле:
        //SMMA (i) = (S1 - SMMA (i - 1) + CL (i)) / X
        //Где:
        //S - сумма;
        //S1 - сумма цен закрытия N периодов, отсчитываемая от предыдущего бара;
        //SMMA (i - 1) - сглаженное скользящее среднее предыдущего бара;
        //SMMA (i) - сглаженное скользящее среднее текущего бара (кроме первого);
        //CL (i) - текущая цена закрытия;
        //X - период сглаживания. 
        //iMA(NULL, PERIOD_M15, iMA_Period, 0, MODE_SMMA, PRICE_CLOSE, 1);
        public double iMA(Period timeframe, int IMA_Period, int shift)//Расчет скользящего среднего. 
        {
            double ima = 0;

            double S1 = Sum(timeframe, IMA_Period, shift);
            double SMMA1 = S1 / IMA_Period;

            ima = (S1 - simpleMovingAvg(timeframe, IMA_Period, shift + 1) + (double)CandleWSRTForFormula(timeframe, shift).Close) / IMA_Period;

            ima = Math.Round(ima, Digits);

            return ima;
        }

        double simpleMovingAvg(Period timeframe, int period, int shift)
        {
            double sma = 0;
            double sum = 0;

            for (int i = period + shift; i > shift; i--)
            {
                sum += (double)CandleWSRTForFormula(timeframe, i).Close;//iClose(timeframe, i);
            }

            sma = sum / period;

            return sma;
        }

        double Sum(Period timeframe, int period, int shift)
        {
            double sum = 0;

            for (int i = period + shift; i > shift; i--)
            {
                sum += (double)CandleWSRTForFormula(timeframe, i).Close;//iClose(timeframe, i);
            }

            return sum;
        }

//Индекс Товарного Канала (Commodity Channel Index, CCI) измеряет отклонение цены инструмента от его среднестатистической цены. Слишком высокие значения индекса (больше +100) указывают на то, что цена находится в зоне перекупленности, а слишком низкие значения (меньше -100) говорят о том, что цена находится в зоне перепроданности.
//Технология расчета индикатора Commodity Channel Index (CCI):
//1. Найти типичную цену. Для этого необходимо сложить максимум, минимум и цену закрытия каждого бара и разделить сумму на 3:
//  TP = (HIGH + LOW + CLOSE) / 3
//2. Вычислить n-периодное простое скользящее среднее типичных цен:
//  SMA (TP, N) = SUM (TP, N) / N
//3. Вычесть полученное SMA(TP, N) из типичных цен TP каждого из предшествующих n периодов:
//  D = TP - SMA (TP, N)
//4. Вычислить n-периодное простое скользящее среднее абсолютных значений D:
//  SMA (D, N) = SUM (D, N) / N
//5. Умножить полученное SMA (D, N) на 0,015:
//  M = SMA (D, N) * 0,015
//6. Разделить M на D:
//  CCI = M / D
//Где:
//HIGH - максимальная цена бара;
//LOW - минимальная цена бара;
//CLOSE - цена закрытия;
//SMA - простое скользящее среднее;
//SUM - сумма;
//N - количество периодов, используемых для расчета.
        double iCCI(Period timeframe, int period, int shift)//Расчет индикатора Commodity Channel Index. 
        {
            double icci = 0;

            double tpCur = TypicalPrice(timeframe, period, shift);
            double smaTp=SimpleMAofTypicalPrice(timeframe, period, shift);

            double a = tpCur - smaTp;
            double b = 0.015 * MeanDeviation(timeframe, period, shift);
            //double b = 0.015 * MeanDeviation(timeframe, period, shift);

            icci = a / b;

            icci = Math.Round(icci, Digits);

            return icci;
        }

        double TypicalPrice(Period timeframe, int period, int shift)
        {
            double tp = (double)(CandleWSRTForFormula(timeframe, shift).High + CandleWSRTForFormula(timeframe, shift).Low + CandleWSRTForFormula(timeframe, shift).Close) / 3;

            return tp;
        }

        double SimpleMAofTypicalPrice(Period timeframe, int period, int shift, bool flag=false)
        {
            double sum = 0;

            if (!flag)
            {
                for (int i = period + shift; i > shift; i--)
                {
                    sum += TypicalPrice(timeframe, period, i);
                }
            }
            else
            {
                for (int i = period + shift-1; i >= shift; i--)
                {
                    sum += TypicalPrice(timeframe, period, i);
                }
            }

            sum = sum / period;

            return sum;
        }

        double MeanDeviation(Period timeframe, int period, int shift)
        {
            double md = 0;


            for (int i = period + shift; i > shift; i--)
            {
                md += Math.Abs(SimpleMAofTypicalPrice(timeframe, period, i, true) - TypicalPrice(timeframe, period, i));
            }

            md = md / period;

            return md;
        }





    }
}
